from PyQt4.QtCore import *
from PyQt4.QtGui import *

from Tileset import *
from EntityFactory import EntityFactory

ZI_TILE = 10
ZI_ENTITIES = 20
ZI_GRID = 30
ZI_CURSOR = 40

class MapScene(QGraphicsScene):

	class Tile(QGraphicsPixmapItem):
		def __init__(self, qimage, id):
			QGraphicsPixmapItem.__init__(self)
			self.setPixmap(QPixmap.fromImage(qimage))
			self.setZValue(ZI_TILE)
			self.id = id

		def get_id(self):
			return self.id

		def change_image(self, qimage, id=None):
			self.setPixmap(QPixmap.fromImage(qimage))
			if id:
				self.id = id

	class Mob(QGraphicsPixmapItem):
		def __init__(self, id):
			QGraphicsPixmapItem.__init__(self)
			self.id = id
			self.setZValue(ZI_ENTITIES)


	MODE_TILE_EDITION = 1
	MODE_ADD_ENTITY   = 2

	def __init__(self):
		QGraphicsScene.__init__(self)
		self.cursor = None
		self.build_cursor()
		self.grid = []
		self.tiles = []
		self.mobs = []

		self.nb_lines = -1
		self.nb_cols = -1
		self.current_tile_id = 0
		self.mouse_down = False

		self.mode = MapScene.MODE_TILE_EDITION

	def set_size(self, width, height):
		self.nb_lines = height
		self.nb_cols = width

	##
	# Load and display a zone
	# @zone: MapWidget.Zone model
	#
	def load_zone(self, zone, tileset):
		# remove all tiles and entities
		self.clear()
		# create tile pixmaps from tileset and insert them in the scene
		for i, tile_id in enumerate(zone.tiles):
			img_tile = tileset.get_tile_by_id(tile_id)
			tile = MapScene.Tile(img_tile, tile_id)
			x, y = self.index_to_pos(i)
			tile.setPos(x, y)
			self.addItem(tile)
			self.tiles.append(tile)

		# load mobs
		for map_mob in zone.mobs:
			self.add_mob(map_mob)


	def set_tile(self, index, image, tile_id):
		tile = self.tiles[index]
		tile.change_image(image, tile_id)


	def refresh(self, tileset):
		for index, tile in enumerate(self.tiles):
			image = tileset.get_tile_by_id(tile.get_id())
			tile.change_image(image)
			x, y = self.index_to_pos(index)
			tile.setPos(x, y)
		# refresh cursor
		self.build_cursor()
		# refresh grid
		self.build_grid()

	##
	# Create grid and insert lines in the scene
	#
	def build_grid(self):
		# remove the old grid, if any
		for line in self.grid:
			self.removeItem(line)
		del self.grid[:]

		# build vertical lines
		for i in xrange(1, self.nb_cols):
			line = QGraphicsLineItem(i * Tileset.TILESIZE, 0, i * Tileset.TILESIZE, self.nb_lines * Tileset.TILESIZE)
			line.setZValue(ZI_GRID)
			self.addItem(line)
			self.grid.append(line)

		# build horizontal lines
		for i in xrange(1, self.nb_lines):
			line = QGraphicsLineItem(0, i * Tileset.TILESIZE, self.nb_cols * Tileset.TILESIZE, i * Tileset.TILESIZE)
			line.setZValue(ZI_GRID)
			self.addItem(line)
			self.grid.append(line)


	def show_grid(self, visible):
		for line in self.grid:
			line.setVisible(visible)

	##
	# Remove all zone elements from the scene
	#
	def clear(self):
		# clear tiles
		for tile in self.tiles:
			self.removeItem(tile)
		self.tiles = []
		# clear mobs
		for mob in self.mobs:
			self.removeItem(mob)
		self.mobs = []


	##
	# Add selection cursor in the scene
	#
	def build_cursor(self):
		# remove cursor, if any
		if self.cursor != None:
			self.removeItem(self.cursor)

		rect = QRectF(0, 0, Tileset.TILESIZE, Tileset.TILESIZE)
		pen = QPen(Qt.red, 1, Qt.SolidLine)
		brush = QBrush(QColor.fromRgb(255, 0, 0, 128))

		self.cursor = self.addRect(rect, pen, brush)
		self.cursor.setVisible(True)
		self.cursor.setZValue(ZI_CURSOR)


	def get_tile_at_mouse(self, event):
		x = int(event.scenePos().x()) / Tileset.TILESIZE
		y = int(event.scenePos().y()) / Tileset.TILESIZE
		if 0 <= x < self.nb_cols and 0 <= y < self.nb_lines:
			self.cursor.setPos(x * Tileset.TILESIZE, y * Tileset.TILESIZE)
			return y * self.nb_cols + x + 1
		return 0


	def index_to_pos(self, index):
		x = (index % self.nb_cols) * Tileset.TILESIZE
		y = (index / self.nb_cols) * Tileset.TILESIZE
		return x, y


	def mouseMoveEvent(self, event):
		if self.mode == MapScene.MODE_TILE_EDITION:
			tile_id = self.get_tile_at_mouse(event)
			if tile_id != 0:
				self.current_tile_id = tile_id
				if self.mouse_down:
					self.emit(SIGNAL("tile_selected"), tile_id)
		elif self.mode == MapScene.MODE_ADD_ENTITY:
			x = event.scenePos().x()
			y = event.scenePos().y()
			self.current_mob.setPos(x, y)

	def mousePressEvent(self, event):
		if event.button() == Qt.LeftButton:
			if self.mode == MapScene.MODE_TILE_EDITION:
				tile_id = self.get_tile_at_mouse(event)
				if tile_id != 0:
					self.current_tile_id = tile_id
					self.emit(SIGNAL("tile_selected"), tile_id)
					if event.button() == Qt.LeftButton:
						self.mouse_down = True
			elif self.mode == MapScene.MODE_ADD_ENTITY:
				x = event.scenePos().x()
				y = event.scenePos().y()
				self.emit(SIGNAL("entity_added"), self.current_mob.id, x, y)



	def mouseReleaseEvent(self, event):
		if event.button() == Qt.LeftButton:
			self.mouse_down = False

	def set_current_entity(self, entity_id):
		mob = MapScene.Mob(entity_id)
		#mob.setPos(item.x, item.y)

		pixmap = EntityFactory().get_mob(entity_id).image
		mob.setPixmap(pixmap)
		mob.setOpacity(0.5)
		self.addItem(mob)
		self.current_mob = mob

		self.mode = MapScene.MODE_ADD_ENTITY

	##
	# Add a mob in the scene
	# @param map_mob: MapWidget.Entity
	#
	def add_mob(self, map_mob):
		# create scene item
		mob = MapScene.Mob(map_mob.id)
		mob.setPos(map_mob.x, map_mob.y)
		# retrieve pixmap from factory
		mob_definition = EntityFactory().get_mob(map_mob.id)
		mob.setPixmap(mob_definition.image)
		# insert in scene
		self.addItem(mob)
		self.mobs.append(mob)

	##
	# Remove a mob by its position in the container
	#
	def remove_mob_at(self, index):
		item = self.mobs[index]
		self.removeItem(item)
		del self.mobs[index]
